package com.hung.triple.transport.netty4.client;

import com.hung.triple.transport.netty4.handler.NettyChannelInitializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.Objects;


/**
 * Netty Client 连接器
 * 注：每一个NettyClient实例都会起一组 EventGroup 线程进行轮询，建议复用，只存在一个 NettyClient 实例
 * <p>
 * Client有一个EventGroup当作发送RPC请求的线程池，复用了与Server的连接，避免每一次RPC调用都要生成一个TCP连接
 * 同时Server也有Selector来对Client来的连接进行复用
 *
 * @author Hung
 * @since 2022/9/18
 */
@Slf4j
public class NettyClient {

    private Bootstrap clientBootstrap;
    private EventLoopGroup workerGroup;

    @SneakyThrows
    public NettyClient() {
        //获取ssl证书
        File cacertFile = new File(Objects.requireNonNull(this.getClass().getResource("/consumer/cacert.pem")).toURI());
        File clientKeyFile = new File(Objects.requireNonNull(this.getClass().getResource("/consumer/client-key.pem")).toURI());
        File clientCertFile = new File(Objects.requireNonNull(this.getClass().getResource("/consumer/clientCert.pem")).toURI());

        SslContext sslContext = SslContextBuilder.forClient().keyManager(clientCertFile, clientKeyFile).trustManager(cacertFile).build();

        // 默认线程数，CPU核心数 * 2
        workerGroup = new NioEventLoopGroup();
        clientBootstrap = new Bootstrap();
        clientBootstrap.group(workerGroup)
                .channel(NioSocketChannel.class)
                .handler(new NettyChannelInitializer(true, sslContext, true));
    }

    /**
     * 连接远程服务器
     */
    public Channel connect(String ip, int port) throws Exception {
        ChannelFuture channelFuture = clientBootstrap.connect(ip, port).sync();
        log.info("[Triple] netty client connect to {}:{} success", ip, port);
        return channelFuture.channel();
    }

    /**
     * 关闭 Netty
     */
    public void stop() {
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
    }

}
